/*
 * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with
 * the License. You may obtain a copy of the License at
 *
 * http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on
 * an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the
 * specific language governing permissions and limitations under the License.
 */
package org.flowable.ui.common.rest.idm.remote;

import java.util.ArrayList;
import java.util.Collection;
import java.util.List;
import java.util.stream.Collectors;

import org.flowable.common.engine.api.FlowableIllegalStateException;
import org.flowable.idm.api.Group;
import org.flowable.idm.api.IdmIdentityService;
import org.flowable.idm.api.Privilege;
import org.flowable.idm.api.User;
import org.flowable.ui.common.model.GroupRepresentation;
import org.flowable.ui.common.model.RemoteUser;
import org.flowable.ui.common.model.UserRepresentation;
import org.flowable.ui.common.rest.idm.CurrentUserProvider;
import org.flowable.ui.common.security.DefaultPrivileges;
import org.flowable.ui.common.service.exception.NotFoundException;
import org.flowable.ui.common.service.idm.RemoteIdmService;
import org.springframework.beans.factory.InitializingBean;
import org.springframework.beans.factory.ObjectProvider;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.security.core.Authentication;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;

/**
 * @author qinman
 * @author zhangchongjie
 * @date 2023/01/03
 */
@RestController
@RequestMapping({"/app", "/"})
public class RemoteAccountResource implements InitializingBean {

    private static final String ADMIN_KEY = "admin";
    protected final Collection<CurrentUserProvider> currentUserProviders;
    @Autowired(required = false)
    private RemoteIdmService remoteIdmService;

    @Autowired(required = false)
    private IdmIdentityService identityService;

    public RemoteAccountResource(ObjectProvider<CurrentUserProvider> currentUserProviders) {
        this.currentUserProviders = currentUserProviders.orderedStream().collect(Collectors.toList());
    }

    @Override
    public void afterPropertiesSet() {
        if (remoteIdmService == null && identityService == null) {
            throw new FlowableIllegalStateException("No remoteIdmService or identityService have been provided");
        }
    }

    /**
     * GET /rest/account -> get the current user.
     */
    @GetMapping(value = "/rest/account", produces = "application/json")
    public UserRepresentation getAccount(Authentication authentication) {
        UserRepresentation userRepresentation = new UserRepresentation();
        userRepresentation.setFirstName(ADMIN_KEY);
        userRepresentation.setLastName(ADMIN_KEY);
        userRepresentation.setFullName(ADMIN_KEY);
        userRepresentation.setId(ADMIN_KEY);
        List<String> pris = new ArrayList<>();
        pris.add(DefaultPrivileges.ACCESS_MODELER);
        pris.add(DefaultPrivileges.ACCESS_IDM);
        pris.add(DefaultPrivileges.ACCESS_ADMIN);
        pris.add(DefaultPrivileges.ACCESS_TASK);
        pris.add(DefaultPrivileges.ACCESS_REST_API);
        userRepresentation.setPrivileges(pris);
        for (CurrentUserProvider userProvider : currentUserProviders) {
            if (userProvider.supports(authentication)) {
                userRepresentation = userProvider.getCurrentUser(authentication);
            }

            if (userRepresentation != null) {
                break;
            }

        }

        if (userRepresentation == null) {
            userRepresentation = getCurrentUserRepresentation(authentication.getName());
        }

        if (userRepresentation != null) {
            return userRepresentation;
        } else {
            throw new NotFoundException();
        }
    }

    protected UserRepresentation getCurrentUserRepresentation(String currentUserId) {
        if (remoteIdmService != null) {
            return createUserRepresentationFromRemoteService(currentUserId);
        } else {
            return createUserRepresentationFromIdentityService(currentUserId);
        }
    }

    /**
     * 从远程IDM服务创建用户表示
     */
    private UserRepresentation createUserRepresentationFromRemoteService(String currentUserId) {
        RemoteUser remoteUser = remoteIdmService.getUser(currentUserId);
        if (remoteUser == null) {
            return null;
        }
        UserRepresentation userRepresentation = new UserRepresentation(remoteUser);
        // 设置用户组信息
        if (remoteUser.getGroups() != null && !remoteUser.getGroups().isEmpty()) {
            List<GroupRepresentation> groups =
                remoteUser.getGroups().stream().map(GroupRepresentation::new).collect(Collectors.toList());
            userRepresentation.setGroups(groups);
        }
        // 设置用户权限信息
        if (remoteUser.getPrivileges() != null && !remoteUser.getPrivileges().isEmpty()) {
            userRepresentation.setPrivileges(remoteUser.getPrivileges());
        }
        return userRepresentation;
    }

    /**
     * 从身份服务创建用户表示
     */
    private UserRepresentation createUserRepresentationFromIdentityService(String currentUserId) {
        User user = identityService.createUserQuery().userId(currentUserId).singleResult();
        if (user == null) {
            return null;
        }
        UserRepresentation userRepresentation = new UserRepresentation(user);
        // 设置用户组信息
        List<Group> userGroups = identityService.createGroupQuery().groupMember(currentUserId).list();
        if (!userGroups.isEmpty()) {
            List<GroupRepresentation> groups =
                userGroups.stream().map(GroupRepresentation::new).collect(Collectors.toList());
            userRepresentation.setGroups(groups);
        }
        // 设置用户权限信息
        List<Privilege> userPrivileges = identityService.createPrivilegeQuery().userId(currentUserId).list();
        if (!userPrivileges.isEmpty()) {
            List<String> privileges = userPrivileges.stream().map(Privilege::getName).collect(Collectors.toList());
            userRepresentation.setPrivileges(privileges);
        }
        return userRepresentation;
    }
}
